using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Xfinity.Rendering
{

    /// <summary>
    /// A container for all triangle lists to be rendered.
    /// </summary>
    public static class TriangleListsContainer
    {
        #region Lists
        private static List<IWorldViewProjection> lists = new List<IWorldViewProjection>();

        /// <summary>
        /// The triangle lists to be modified.
        /// </summary>
        public static List<IWorldViewProjection> Lists
        {
            [DebuggerStepThrough]
            get { return lists; }
        }

        #endregion
    }
    /// <summary>
    /// A renderable triangle list.
    /// </summary>
    /// <typeparam name="VertexType">The vertex type; eg VertexPositionColor.</typeparam>
    public class TriangleList<VertexType> : IRenderable, IDisposable, IDrawable, IGameComponent where VertexType : struct
    {
        private Game game;
        private VertexElement[] elements;
        private VertexDeclaration declaration;
        private VertexBuffer buffer;
        private GraphicsDevice device;
        private IGraphicsDeviceService service;
        private int size;
        private VertexType[] vertices;
        #region World
        private Matrix world = Matrix.Identity;

        /// <summary>
        /// The world transform.
        /// </summary>
        public Matrix World
        {
            get { return world; }
            set { world = value; }
        }

        #endregion
        #region View
        private Matrix view = Matrix.Identity;

        /// <summary>
        /// The view transform.
        /// </summary>
        public Matrix View
        {
            get { return view; }
            set { view = value; }
        }

        #endregion
        #region Projection
        private Matrix projection;

        /// <summary>
        /// The projection transform.
        /// </summary>
        public Matrix Projection
        {
            get { return projection; }
            set { projection = value; }
        }

        #endregion
        #region Effect
        private Effect effect;

        /// <summary>
        /// The effect to render with.
        /// </summary>
        public Effect Effect
        {
            get { return effect; }
            set { effect = value; }
        }

        #endregion
        /// <summary>
        /// Accesses the triplets of triangles.
        /// </summary>
        /// <param name="index">The zero-based index of the triangle.</param>
        /// <returns>The three vertices.</returns>
        public VertexType[] this[int index]
        {
            get
            {
                int modified = 3 * index;
                return new VertexType[] { vertices[modified], vertices[modified + 1], vertices[modified + 2] };
            }
            set
            {
                int modified = 3 * index;
                vertices[modified] = value[0];
                modified++;
                vertices[modified] = value[1];
                modified++;
                vertices[modified] = value[2];
            }
        }
        /// <summary>
        /// Constructs a new triangle list.
        /// </summary>
        /// <param name="elements">The vertex elements.</param>
        /// <param name="game">The game.</param>
        public TriangleList(VertexElement[] elements, Game game)
        {
            this.elements = elements;
            this.game = game;
            this.maxTriangles = 1024;
            vertices = new VertexType[maxTriangles];
        }
        private static EffectPool effectPool = new EffectPool();
        private void SetUpGraphics()
        {

            this.size = Marshal.SizeOf(typeof(VertexType));
            this.service = (IGraphicsDeviceService)(game.Services.GetService(typeof(IGraphicsDeviceService)));
            this.device = service.GraphicsDevice;
            this.declaration = new VertexDeclaration(service.GraphicsDevice, elements);
            this.buffer = new VertexBuffer(device, size * maxTriangles * 3, BufferUsage.None);//(device, size * maxTriangles * 3, ResourceUsage.None, ResourceManagementMode.Automatic);
            if (effect == null)
            {
                effect = new BasicEffect(device, effectPool);
            }
            if (effect is BasicEffect)
            {
                BasicEffect be = effect as BasicEffect;
                be.EnableDefaultLighting();
            }
        }
        /// <summary>
        /// Constructs a new triangle list.
        /// </summary>
        /// <param name="existing"> The existing list of triangles.</param>
        /// <param name="elements">The vertex elements.</param>
        /// <param name="game">The game.</param>
        public TriangleList(IList<VertexType> existing, VertexElement[] elements, Game game)
        {
            this.elements = elements;
            this.game = game;
            Debug.Assert(existing.Count % 3 == 0);
            int count = existing.Count;
            if ((count % 3) == 0 || count < 3)
            {
                throw new ArgumentException("Existing list must have a multiple of three vertices.", "existing");
            }
            vertices = new VertexType[count];
            this.count = count / 3;
            for (int i = 0; i < count; i++)
            {
                vertices[i] = existing[i];
            }

            this.maxTriangles = count / 3;
        }

        #region MaxTriangles
        private int maxTriangles;

        /// <summary>
        /// The maximum number of triangles that will be added to the list.
        /// </summary>
        public int MaxTriangles
        {
            get { return maxTriangles; }
            set { maxTriangles = value; }
        }

        #endregion
        /// <summary>
        /// Adds a triangle to the list.
        /// </summary>
        /// <param name="v1">The first vertex.</param>
        /// <param name="v2">The second vertex.</param>
        /// <param name="v3">The third vertex.</param>
        /// <returns>The index of the newly created triangle.</returns>
        public int AddTriangle(VertexType v1, VertexType v2, VertexType v3)
        {
            if (count < maxTriangles)
            {
                int modified = 3 * count;
                vertices[modified] = v1;
                modified++;
                vertices[modified] = v2;
                modified++;
                vertices[modified] = v3;
                count++;
                return count;
            }
            else
            {
                throw new InvalidOperationException("Cannot exceed the maximum number of triangles.");
            }
        }
        #region Count
        private int count = 0;

        /// <summary>
        /// The count.
        /// </summary>
        public int Count
        {
            get { return count; }
            set { count = value; }
        }

        #endregion

        #region IDrawable Members

        /// <summary>
        /// Draws the triangle list.
        /// </summary>
        /// <param name="gameTime">The elapsed game time.</param>
        public void Draw(GameTime gameTime)
        {
            VertexDeclaration oldDec = device.VertexDeclaration;

            effect.Begin();
            
            device.VertexDeclaration = declaration;
            buffer.SetData<VertexType>(vertices);
            device.Vertices[0].SetSource(buffer, 0, size);
            if (!(effect is BasicEffect))
            {
                effect.Parameters.GetParameterBySemantic("World").SetValue(world);
                effect.Parameters.GetParameterBySemantic("View").SetValue(view);
                effect.Parameters.GetParameterBySemantic("Projection").SetValue(projection);
            }
            else
            {
                BasicEffect be = (BasicEffect)effect;
                be.World = world;
                be.View = view;
                be.Projection = projection;
            }
            foreach (EffectPass pass in effect.Techniques[0].Passes)
            {
                pass.Begin();
                device.DrawUserPrimitives<VertexType>(PrimitiveType.TriangleList, vertices, 0, count);
                pass.End();
            }

            device.VertexDeclaration = oldDec;
            effect.End();
        }
        private int drawOrder = 0;
        public int DrawOrder
        {
            get { return drawOrder; }
            set { drawOrder = value; }
        }

        public event EventHandler DrawOrderChanged;
        private bool visible = true;
        public bool Visible
        {
            get { return visible; }
            set { visible = value; }
        }

        public event EventHandler VisibleChanged;

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Disposes the object.
        /// </summary>
        public void Dispose()
        {
            declaration.Dispose();
            GC.SuppressFinalize(this);
        }

        #endregion

        #region IGameComponent Members

        /// <summary>
        /// Initialises the triangle list.
        /// </summary>
        public void Initialize()
        {
            SetUpGraphics();
            TriangleListsContainer.Lists.Add(this);
        }

        #endregion

        #region IRenderableEffect Members

        /// <summary>
        /// The effect parameters.
        /// </summary>
        public EffectParameterCollection Parameters
        {
            get
            {
                return effect.Parameters;
            }
        }

        #endregion
    }
}
